"""
健康检查任务
定期检查爬虫健康状态和数据新鲜度
"""
import os
import logging
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional
import motor.motor_asyncio
import asyncio
from src.celery_app import app
from src.task_logger import task_logger, TaskLog, TaskEvent, LogLevel

logger = logging.getLogger(__name__)

class HealthChecker:
    """健康检查器"""
    
    def __init__(self):
        self.mongo_uri = os.getenv('MONGODB_URI', 'mongodb://localhost:27017')
        self.db_name = os.getenv('MONGODB_DB', 'crawler_scheduler')
        self.client = None
        self.db = None
        
        # 健康检查阈值配置
        self.thresholds = {
            'weibo': {'max_age_minutes': 30, 'min_items': 10},
            'zhihu': {'max_age_minutes': 60, 'min_items': 5},
            'toutiao': {'max_age_minutes': 40, 'min_items': 10},
            'baidu': {'max_age_minutes': 30, 'min_items': 10},
            'xiaohongshu': {'max_age_minutes': 90, 'min_items': 5},
            'douyin': {'max_age_minutes': 60, 'min_items': 5},
            'twitter': {'max_age_minutes': 120, 'min_items': 5},
            'reddit': {'max_age_minutes': 120, 'min_items': 10},
            'youtube': {'max_age_minutes': 240, 'min_items': 5},
        }
        
    async def connect(self):
        """连接MongoDB"""
        if not self.client:
            self.client = motor.motor_asyncio.AsyncIOMotorClient(self.mongo_uri)
            self.db = self.client[self.db_name]
            
    async def check_platform_health(self, platform: str) -> Dict[str, Any]:
        """
        检查平台健康状态
        
        Args:
            platform: 平台名称
            
        Returns:
            健康状态
        """
        if not self.db:
            await self.connect()
            
        threshold = self.thresholds.get(platform, {
            'max_age_minutes': 60,
            'min_items': 5
        })
        
        # 获取最新数据时间
        collection = self.db[f'{platform}_hot_items']
        latest_doc = await collection.find_one(
            sort=[('crawled_at', -1)]
        )
        
        health_status = {
            'platform': platform,
            'healthy': True,
            'issues': [],
            'metrics': {},
            'checked_at': datetime.utcnow()
        }
        
        if not latest_doc:
            health_status['healthy'] = False
            health_status['issues'].append('No data found')
            health_status['metrics']['last_update'] = None
            health_status['metrics']['data_age_minutes'] = None
        else:
            last_update = latest_doc.get('crawled_at', datetime.utcnow())
            data_age = datetime.utcnow() - last_update
            data_age_minutes = data_age.total_seconds() / 60
            
            health_status['metrics']['last_update'] = last_update.isoformat()
            health_status['metrics']['data_age_minutes'] = data_age_minutes
            
            # 检查数据新鲜度
            if data_age_minutes > threshold['max_age_minutes']:
                health_status['healthy'] = False
                health_status['issues'].append(
                    f"Data is stale ({data_age_minutes:.1f} minutes old, "
                    f"threshold: {threshold['max_age_minutes']} minutes)"
                )
                
        # 检查数据量
        recent_time = datetime.utcnow() - timedelta(hours=1)
        recent_count = await collection.count_documents({
            'crawled_at': {'$gte': recent_time}
        })
        
        health_status['metrics']['recent_items_count'] = recent_count
        
        if recent_count < threshold['min_items']:
            health_status['healthy'] = False
            health_status['issues'].append(
                f"Insufficient data ({recent_count} items in last hour, "
                f"threshold: {threshold['min_items']} items)"
            )
            
        # 检查最近任务状态
        task_collection = self.db.task_results
        failed_tasks = await task_collection.count_documents({
            'task_name': f'src.tasks.crawler.crawl_{platform}',
            'status': 'failed',
            'timestamp': {'$gte': datetime.utcnow() - timedelta(hours=1)}
        })
        
        health_status['metrics']['recent_failures'] = failed_tasks
        
        if failed_tasks > 3:
            health_status['healthy'] = False
            health_status['issues'].append(
                f"Too many recent failures ({failed_tasks} in last hour)"
            )
            
        return health_status
        
    async def check_all_platforms(self) -> List[Dict[str, Any]]:
        """
        检查所有平台健康状态
        
        Returns:
            所有平台的健康状态
        """
        results = []
        
        for platform in self.thresholds.keys():
            try:
                status = await self.check_platform_health(platform)
                results.append(status)
                
                if not status['healthy']:
                    logger.warning(f"Platform {platform} is unhealthy: {status['issues']}")
                    
            except Exception as e:
                logger.error(f"Error checking {platform} health: {e}")
                results.append({
                    'platform': platform,
                    'healthy': False,
                    'issues': [f'Health check error: {str(e)}'],
                    'metrics': {},
                    'checked_at': datetime.utcnow()
                })
                
        return results
        
    async def check_system_resources(self) -> Dict[str, Any]:
        """
        检查系统资源
        
        Returns:
            系统资源状态
        """
        import psutil
        
        resources = {
            'cpu_percent': psutil.cpu_percent(interval=1),
            'memory_percent': psutil.virtual_memory().percent,
            'disk_percent': psutil.disk_usage('/').percent,
            'healthy': True,
            'issues': []
        }
        
        # 检查CPU使用率
        if resources['cpu_percent'] > 80:
            resources['healthy'] = False
            resources['issues'].append(f"High CPU usage: {resources['cpu_percent']}%")
            
        # 检查内存使用率
        if resources['memory_percent'] > 85:
            resources['healthy'] = False
            resources['issues'].append(f"High memory usage: {resources['memory_percent']}%")
            
        # 检查磁盘使用率
        if resources['disk_percent'] > 90:
            resources['healthy'] = False
            resources['issues'].append(f"High disk usage: {resources['disk_percent']}%")
            
        return resources

@app.task(name='src.tasks.health.health_check')
def health_check():
    """健康检查任务"""
    checker = HealthChecker()
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    
    try:
        # 检查所有平台
        platform_results = loop.run_until_complete(checker.check_all_platforms())
        
        # 检查系统资源
        system_resources = loop.run_until_complete(checker.check_system_resources())
        
        # 汇总结果
        unhealthy_platforms = [p for p in platform_results if not p['healthy']]
        
        result = {
            'timestamp': datetime.utcnow().isoformat(),
            'overall_healthy': len(unhealthy_platforms) == 0 and system_resources['healthy'],
            'platforms': platform_results,
            'system_resources': system_resources,
            'unhealthy_count': len(unhealthy_platforms)
        }
        
        # 如果有不健康的平台，触发告警
        if unhealthy_platforms:
            alert_manager.send_health_alert(unhealthy_platforms)
            
        # 记录日志
        loop.run_until_complete(
            task_logger.log_task_complete(
                task_id=health_check.request.id,
                task_name='health_check',
                result=result
            )
        )
        
        return result
        
    except Exception as e:
        logger.error(f"Health check failed: {e}")
        loop.run_until_complete(
            task_logger.log_task_failure(
                task_id=health_check.request.id,
                task_name='health_check',
                error=e
            )
        )
        raise
    finally:
        loop.close()

@app.task(name='src.tasks.health.check_data_freshness')
def check_data_freshness():
    """数据新鲜度检查任务"""
    checker = HealthChecker()
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    
    try:
        stale_platforms = []
        
        for platform in checker.thresholds.keys():
            status = loop.run_until_complete(checker.check_platform_health(platform))
            
            if status['metrics'].get('data_age_minutes'):
                threshold = checker.thresholds[platform]['max_age_minutes']
                
                if status['metrics']['data_age_minutes'] > threshold * 2:
                    # 数据严重过期（超过阈值2倍）
                    stale_platforms.append({
                        'platform': platform,
                        'age_minutes': status['metrics']['data_age_minutes'],
                        'threshold': threshold,
                        'severity': 'critical'
                    })
                elif status['metrics']['data_age_minutes'] > threshold:
                    # 数据过期
                    stale_platforms.append({
                        'platform': platform,
                        'age_minutes': status['metrics']['data_age_minutes'],
                        'threshold': threshold,
                        'severity': 'warning'
                    })
                    
        result = {
            'timestamp': datetime.utcnow().isoformat(),
            'stale_platforms': stale_platforms,
            'fresh_count': len(checker.thresholds) - len(stale_platforms),
            'stale_count': len(stale_platforms)
        }
        
        # 发送告警
        if stale_platforms:
            critical_platforms = [p for p in stale_platforms if p['severity'] == 'critical']
            if critical_platforms:
                alert_manager.send_critical_alert(critical_platforms)
                
        return result
        
    except Exception as e:
        logger.error(f"Data freshness check failed: {e}")
        raise
    finally:
        loop.close()

class AlertManager:
    """告警管理器"""
    
    def __init__(self):
        self.webhook_url = os.getenv('ALERT_WEBHOOK_URL')
        self.email_config = {
            'smtp_host': os.getenv('SMTP_HOST', 'smtp.gmail.com'),
            'smtp_port': int(os.getenv('SMTP_PORT', 587)),
            'smtp_user': os.getenv('SMTP_USER'),
            'smtp_password': os.getenv('SMTP_PASSWORD'),
            'alert_recipients': os.getenv('ALERT_RECIPIENTS', '').split(',')
        }
        
    def send_health_alert(self, unhealthy_platforms: List[Dict]):
        """发送健康告警"""
        message = self._format_health_alert(unhealthy_platforms)
        
        # 发送webhook
        if self.webhook_url:
            self._send_webhook(message)
            
        # 发送邮件
        if self.email_config['smtp_user']:
            self._send_email("爬虫健康检查告警", message)
            
        logger.warning(f"Sent health alert for {len(unhealthy_platforms)} platforms")
        
    def send_critical_alert(self, critical_platforms: List[Dict]):
        """发送严重告警"""
        message = self._format_critical_alert(critical_platforms)
        
        # 发送webhook
        if self.webhook_url:
            self._send_webhook(message, level='critical')
            
        # 发送邮件
        if self.email_config['smtp_user']:
            self._send_email("【严重】爬虫数据过期告警", message)
            
        logger.error(f"Sent critical alert for {len(critical_platforms)} platforms")
        
    def _format_health_alert(self, unhealthy_platforms: List[Dict]) -> str:
        """格式化健康告警消息"""
        lines = ["爬虫健康检查发现以下问题：\n"]
        
        for platform in unhealthy_platforms:
            lines.append(f"平台: {platform['platform']}")
            for issue in platform['issues']:
                lines.append(f"  - {issue}")
            lines.append("")
            
        lines.append(f"检查时间: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
        return "\n".join(lines)
        
    def _format_critical_alert(self, critical_platforms: List[Dict]) -> str:
        """格式化严重告警消息"""
        lines = ["严重：以下平台数据严重过期：\n"]
        
        for platform in critical_platforms:
            age_hours = platform['age_minutes'] / 60
            lines.append(
                f"平台: {platform['platform']} - "
                f"数据已过期 {age_hours:.1f} 小时 "
                f"(阈值: {platform['threshold']/60:.1f} 小时)"
            )
            
        lines.append(f"\n请立即检查爬虫服务！")
        lines.append(f"时间: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} UTC")
        return "\n".join(lines)
        
    def _send_webhook(self, message: str, level: str = 'warning'):
        """发送webhook告警"""
        import requests
        
        try:
            payload = {
                'text': message,
                'level': level,
                'timestamp': datetime.utcnow().isoformat()
            }
            
            response = requests.post(self.webhook_url, json=payload, timeout=10)
            response.raise_for_status()
            
        except Exception as e:
            logger.error(f"Failed to send webhook alert: {e}")
            
    def _send_email(self, subject: str, body: str):
        """发送邮件告警"""
        import smtplib
        from email.mime.text import MIMEText
        from email.mime.multipart import MIMEMultipart
        
        if not self.email_config['alert_recipients']:
            return
            
        try:
            msg = MIMEMultipart()
            msg['From'] = self.email_config['smtp_user']
            msg['To'] = ', '.join(self.email_config['alert_recipients'])
            msg['Subject'] = subject
            
            msg.attach(MIMEText(body, 'plain', 'utf-8'))
            
            with smtplib.SMTP(self.email_config['smtp_host'], 
                             self.email_config['smtp_port']) as server:
                server.starttls()
                server.login(self.email_config['smtp_user'], 
                           self.email_config['smtp_password'])
                server.send_message(msg)
                
        except Exception as e:
            logger.error(f"Failed to send email alert: {e}")

# 创建全局告警管理器
alert_manager = AlertManager()